package com.gitee.loyo.act.remote

import com.gitee.loyo.Helper
import com.gitee.loyo.KSQL
import com.gitee.loyo.Utils
import com.gitee.loyo.act.*
import com.gitee.loyo.interfaces.TypeReference
import kotlin.reflect.KClass
import kotlin.reflect.full.companionObjectInstance
import kotlin.reflect.full.isSubclassOf

object DualModels {

    lateinit var getClassName: (String) -> String

    lateinit var execute: (ModelExecution) -> String

    lateinit var invoke: (RemoteInvocation) -> String

    lateinit var mapper: Mapper

}

interface Mapper{

    fun <T: Any> serialize(model: T): String

    fun <T: Any> mapping(string: String, typeReference: TypeReference<T>): T

    fun <T: Any> mappingList(string: String, typeReference: TypeReference<T>): List<T>

}

data class ModelExecution(
    val modelName: String,
    val modelSQLBuilder: ModelSQLBuilder<*, *>,
    val crud: Crud
){

    fun execute(): String{
        val modelKClass = Class.forName(DualModels.getClassName(modelName)).kotlin as KClass<Model<*, *>>
        val model = com.gitee.loyo.Helper.createModel(modelKClass, modelSQLBuilder.pattern)

        KSQL.reflection.overrideProperties(modelSQLBuilder, model)
        val transferor = (modelKClass.companionObjectInstance as ModelCompanion<*, *>).getTransferorFactory().transfer(model)
        return when(crud){
            Crud.Add -> transferor.insert().toString()
            Crud.Create -> transferor.create()?.toString()?:""
            Crud.Read -> transferor.asLazyReadMapper().asList(Map::class)?.let { DualModels.mapper.serialize(it) }
            Crud.Update -> transferor.update().toString()
            Crud.Delete -> transferor.delete().toString()
        }
    }

    companion object{

        fun doAdd(model: Model<*, *>) = ModelExecution(
            model::class.java.name,
            model,
            Crud.Add
        )

        fun doCreate(model: Model<*, *>) = ModelExecution(
            model::class.java.name,
            model,
            Crud.Create
        )

        fun doRead(model: Model<*, *>) = ModelExecution(
            model::class.java.name,
            model,
            Crud.Read
        )

        fun doUpdate(model: Model<*, *>) = ModelExecution(
            model::class.java.name,
            model,
            Crud.Update
        )

        fun doDelete(model: Model<*, *>) = ModelExecution(
            model::class.java.name,
            model,
            Crud.Delete
        )
    }
}

enum class Crud{
    Add, Create, Read, Update, Delete
}

data class RemoteInvocation(
    val className: String,
    val daoClassName: String,
    val methodSign: String,
    val args: Map<String, Any?>
){

    fun invoke(): Any?{
        val cls = Class.forName(DualModels.getClassName(className))
        if(cls.kotlin.isSubclassOf(Record::class)){
            val record = KSQL.reflection.newDataInstance(cls.kotlin, emptyMap()) as Record<*, *>
            val daoClass = Class.forName(DualModels.getClassName(daoClassName))
            val method = daoClass.methods.firstOrNull { Utils.sign(it) == methodSign }?:throw NoSuchMethodError("$daoClassName.$methodSign")
            return record.getTransferorFactory().invoke(cls, method, args)
        }else if(cls.kotlin.isSubclassOf(ModelCompanion::class)){
            val modelCompanion = cls.kotlin.objectInstance as ModelCompanion<*, *>
            val daoClass = Class.forName(DualModels.getClassName(daoClassName))
            val method = daoClass.methods.firstOrNull { Utils.sign(it) == methodSign }?:throw NoSuchMethodError("$daoClassName.$methodSign")
            return modelCompanion.getTransferorFactory().invoke(cls, method, args)
        }
        throw UnsupportedOperationException("不支持的类型：${cls.name}")
    }
}